// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef SRC_CONNECTION_H_
#define SRC_CONNECTION_H_

#include <stdint.h>
#include <event2/bufferevent.h>

#include "proto/seven_summits.pb.h"

namespace seven_summits {

class ConnectionQueue;
class Worker;

// Encapsulates a connection with work to be done.
class Connection {
 public:
  // Represents undefined num_request_bytes.
  static uint32_t kUndefined;

  // Encapsulates the actual processing.
  class ProcessingCallback {
   public:
    virtual ~ProcessingCallback() {};
    // Do the processing, possibly asynchronously.
    // Implementation must call connection->WriteReponse().
    virtual void ProcessAsync(const SmtRequest& request,
                              SmtResponse* response,
                              Connection* connection) = 0;
  };

  Connection();
  virtual ~Connection();

  // Sets connection pool to which to add this instance upon Close().
  void set_pool(ConnectionQueue* pool) { pool_ = pool; }

  // Only used in testing.
  void set_callback(ProcessingCallback* callback) {
    callback_ = callback; 
  }

  // @param callback invoked when request comes in.
  //                 Can be NULL. Ownership remains with caller.
  // @post  state == INITIAL
  void Init(struct event_base* base, int fd, ProcessingCallback* callback);

  // Writes response to the bufferevent.
  void WriteResponse();

  // Closes underlying transport, adds connection to free pool.
  void Close();

 private:
  friend class ConnectionTest;

  // Pure virtual wrapper class, for mocking and readability.
  // All libevent2 calls happen from here.
  class BufferEvent {
   public:
    BufferEvent(struct event_base* base, int sfd, Connection* connection);
    virtual ~BufferEvent();

    // Returns true if `expected' bytes can be consumed.
    virtual bool ReceivedMoreThan(size_t expected);
    // Reads `expected' bytes into buffer `dest'
    virtual size_t Read(void* dest, size_t num_bytes);
    // Writes out the entire respnose. Sets write callback.
    virtual void Write(const void* src, size_t num_bytes);

   protected:
    BufferEvent() : bev_(NULL) {}

   private:
    struct event_base* base_;
    int sfd_;
    Connection* connection_;
    struct bufferevent* bev_;
  };

  enum ConnectionState {
    INITIAL,
    READ_MSG,
    WRITE_MSG
  };

  // Parses the request from the buffer event.
  // @pre  state == READ_MSG
  // @pre  num_request_bytes_ must be set.
  // @pre  `num_request_bytes_' available for consuming.
  // @post smt_request_ contains the parsed request.
  bool ParseRequest();

  static void handle_read(struct bufferevent *bev, void *ptr);
  static void handle_error(struct bufferevent *bev, short error, void *ptr);

  // Callback method used internally.
  // If callback_ is set and the request has been read, then this method calls
  // callback_->ProcessRequestAsync.
  void HandleRead();
  // Callback method used internally.
  void HandleError(short error);

  // Testing only constructor. Does not take ownership of buffer_event.
  Connection(BufferEvent* buffer_event);
  // Testing only.
  uint32_t num_request_bytes() { return num_request_bytes_; }
  // Testing only.
  ConnectionState state() { return state_; }

  BufferEvent* buffer_event_;
  ConnectionState state_;
  ProcessingCallback* callback_;

  uint32_t num_request_bytes_;
  SmtRequest smt_request_;
  SmtResponse smt_response_;

  ConnectionQueue* pool_; 
};

}  // namespace seven_summits

#endif  // SRC_CONNECTION_H_
